from django.conf import settings

from urllib.parse import urlencode, parse_qs
from urllib.request import urlopen
from .exceptions import QQAPIException
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer, BadData
from . import constants
import logging, json
import ssl

logger = logging.getLogger('django')


# QQ登录的工具类
class OAuthQQ(object):
    # 定义初始化方法， 接收所有工具需要使用的参数
    def __init__(self, client_id=None, client_secret=None, redirect_uri=None, state=None):
        self.client_id = client_id or settings.QQ_CLIENT_ID
        self.client_secret = client_secret or settings.QQ_CLIENT_SECRET
        self.redirect_uri = redirect_uri or settings.QQ_REDIRECT_URI
        self.state = state or settings.QQ_STATE

    # 提供QQ扫码登录链接
    def get_qq_login_url(self):
        # 准备链接, 自己补充问号
        login_url = 'https://graph.qq.com/oauth2.0/authorize?'
        # 准备参数
        params = {
            'response_type': 'code',  # 扫码后获得的code
            'client_id': self.client_id,  # 创建应用的标识
            'redirect_uri': self.redirect_uri,  # 扫码成功后跳转的页面（确认登录的页面）
            'state': self.state,  # 登录成功后跳转的页面
            'scope': 'get_user_info',  # 扫码为了最终获取用户的信息（openid）
        }
        # 拼接url
        login_url += urlencode(params)
        return login_url

    # 通过code来获取access_token
    def get_access_token(self, code):
        # 准备url
        url = 'https://graph.qq.com/oauth2.0/token?'
        # 准备参数
        params = {
            'grant_type': 'authorization_code',
            'client_id': self.client_id,
            'client_secret': self.client_secret,
            'code': code,
            'redirect_uri': self.redirect_uri,
        }

        # 拼接url
        url += urlencode(params)
        try:
            # ????????????????????????????使用ssl创建未经验证的上下文，在urlopen中传入上下文参数
            context = ssl._create_unverified_context()
            # 使用code向QQ服务器索取access_token
            response = urlopen(url, context=context)
            # 获取响应二进制
            response_data = response.read()
            # 将响应二进制解码
            response_str = response_data.decode()
            # access_token = FE04 ** ** ** ** ** ** ** ** ** ** ** ** CCE2 & expires_in = 7776000 & refresh_token = 88E4 ** ** ** ** ** ** ** ** ** ** ** ** BE14

            # 将查询字符串转成字典
            response_dict = parse_qs(response_str)
            # 获取access_token
            access_token = response_dict.get('access_token')[0]
        except Exception as e:
            logger.error(e)

            raise QQAPIException('获取access_token失败')
        # 返回access_token
        return access_token

    # 通过access_token来获取openid
    def get_openid(self, access_token):
        # 准备url
        url = 'https://graph.qq.com/oauth2.0/me?access_token=' + access_token

        response_str = ''

        try:
            context = ssl._create_unverified_context()
            response = urlopen(url, context=context)
            response_str = response.read().decode()
            # 返回的数据格式为callback( {"client_id":"YOUR_APPID","openid":"YOUR_OPENID"} );
            # 需要截取字符串并转成字典
            response_dict = json.loads(response_str[10:-4])
            # 获取openid
            openid = response_dict.get('openid')
        except Exception as e:
            # 如果有异常，QQ服务器器返回 "code=xxx&msg=xxx"
            data = parse_qs(response_str)
            logger.error(e)
            raise QQAPIException('code=%s msg=%s' % (data.get('code'), data.get('msg')))

        return openid

    @staticmethod
    def generate_save_user_token(openid):
        """生成保护用户数据签名后的token(静态方法)"""
        serializer = Serializer(settings.SECRET_KEY, expires_in=constants.SAVE_QQ_USER_TOKEN_EXPIRES)
        data = {'openid': openid}
        token = serializer.dumps(data)
        return token.decode()

    @staticmethod
    def check_save_user_token(token):
        """对签名后的openid进行还原(静态方法)"""
        serializer = Serializer(settings.SECRET_KEY, expires_in=constants.SAVE_QQ_USER_TOKEN_EXPIRES)
        try:
            data = serializer.loads(token)
        except BadData:
            return None
        else:
            return data.get('openid')
